package mybatis.proxy;

import mybatis.annotation.MyParam;
import mybatis.annotation.MySelect;
import mybatis.config.MyIntegerTypeHandler;
import mybatis.config.MyStringTypeHandler;
import mybatis.config.MyTypeHandler;
import mybatis.parse.MyParser;

import java.lang.reflect.*;
import java.sql.*;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 自定义代理工厂
 */
public class MyProxyFactory {
    /**
     * 类型处理器Map
     */
    private static final Map<Class<?>, MyTypeHandler<?>> typeHandlerMap = new HashMap<>();

    static {
        typeHandlerMap.put(String.class, new MyStringTypeHandler());
        typeHandlerMap.put(Integer.class, new MyIntegerTypeHandler());

        try {
            Class.forName("com.mysql.cj.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取代理对象
     */
    public static <T> T getProxy(Class<?> interfaceClass) {
        Object proxyInstance = Proxy.newProxyInstance(interfaceClass.getClassLoader(), new Class[]{interfaceClass}, (proxy, method, args) -> {
            //获取注解
            MySelect mySelect = method.getAnnotation(MySelect.class);
            String sql = mySelect.value();
            System.out.println("原始sql：" + sql);

            String parseSql = MyParser.parse(sql);
            System.out.println("解析之后的sql：" + parseSql);

            Map<Integer, String> parameterPositionMap = MyParser.getParameterPositionMap();
            System.out.println("参数位置Map：" + parameterPositionMap);

            Map<String, Object> parameterMap = getParameterMap(method, args);
            System.out.println("参数Map：" + parameterMap);

            try {
                //构建数据库连接
                Connection connection = DriverManager.getConnection("jdbc:mysql://106.14.19.9:3306/dev_wash", "root", "szSJxlnxAxpETFkGjAdTRLQHinAMnfmp");
                //构建预编译数据库操作对象
                PreparedStatement preparedStatement = connection.prepareStatement(parseSql);
                //设置参数
                setParameter(preparedStatement, parameterPositionMap, parameterMap);
                System.out.println("最终执行sql：" + preparedStatement.toString());
                //执行sql
                ResultSet resultSet = preparedStatement.executeQuery();
                //组装返回结果
                return getResult(resultSet, method);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        });
        return (T) proxyInstance;
    }

    /**
     * 获取参数Map
     */
    private static Map<String, Object> getParameterMap(Method method, Object[] args) {
        Map<String, Object> parameterMap = new HashMap<>();
        Parameter[] parameters = method.getParameters();
        for (int i = 0; i < parameters.length; i++) {
            parameterMap.put(parameters[i].getName(), args[i]);
            //获取注解
            MyParam myParam = parameters[i].getAnnotation(MyParam.class);
            String parameterName = myParam.value();
            parameterMap.put(parameterName, args[i]);
        }
        return parameterMap;
    }

    /**
     * 设置参数
     */
    private static void setParameter(PreparedStatement preparedStatement, Map<Integer, String> parameterPositionMap, Map<String, Object> parameterMap) throws SQLException {
        for (int i = 1; i < parameterPositionMap.size() + 1; i++) {
            String parameterName = parameterPositionMap.get(i);
            Object parameter = parameterMap.get(parameterName);
            MyTypeHandler<?> myTypeHandler = typeHandlerMap.get(parameter.getClass());
            myTypeHandler.setParameter(preparedStatement, i, parameter);
        }
    }

    /**
     * 组装返回结果
     */
    private static Object getResult(ResultSet resultSet, Method method) throws SQLException, InstantiationException, IllegalAccessException, NoSuchMethodException, InvocationTargetException {
        Object result;
        List<Object> objects = new ArrayList<>();

        //获取返回对象类型
        Class<?> resultType = getResultType(method);
        System.out.println("返回对象类型：" + resultType);

        //获取返回对象set方法
        Map<String, Method> setMethodMap = new HashMap<>();
        for (Method declaredMethod : resultType.getDeclaredMethods()) {
            if (declaredMethod.getName().startsWith("set")) {
                String propertyName = declaredMethod.getName().substring(3);
                propertyName = propertyName.substring(0, 1).toLowerCase() + propertyName.substring(1);
                setMethodMap.put(propertyName, declaredMethod);
            }
        }
        System.out.println("set方法Map：" + setMethodMap);

        //获取查询结果列名
        List<String> columnNames = new ArrayList<>();
        ResultSetMetaData resultSetMetaData = resultSet.getMetaData();
        for (int i = 1; i < resultSetMetaData.getColumnCount() + 1; i++) {
            columnNames.add(resultSetMetaData.getColumnName(i));
        }
        System.out.println("查询结果列名集合：" + columnNames);

        //构建返回对象
        while (resultSet.next()) {
            //获取返回对象
            Object object = resultType.getDeclaredConstructor().newInstance();
            for (String columnName : columnNames) {
                Method setMethod = setMethodMap.get(columnName);
                Class<?> clazz = setMethod.getParameterTypes()[0];
                MyTypeHandler<?> myTypeHandler = typeHandlerMap.get(clazz);
                Object parameter = myTypeHandler.getResult(resultSet, columnName);
                setMethod.invoke(object, parameter);
            }
            objects.add(object);
        }
        System.out.println("返回对象集合：" + objects);

        if (method.getReturnType().equals(List.class)) {
            result = objects;
        } else {
            result = objects.getFirst();
        }
        System.out.println("返回对象：" + result);

        return result;
    }

    /**
     * 获取返回对象类型
     */
    private static Class<?> getResultType(Method method) {
        Class<?> resultType = null;
        Type genericReturnType = method.getGenericReturnType();
        //非泛型
        if (genericReturnType instanceof Class) {
            resultType = (Class<?>) genericReturnType;
        }
        //泛型
        else if (genericReturnType instanceof ParameterizedType parameterizedType) {
            Type[] actualTypeArguments = parameterizedType.getActualTypeArguments();
            resultType = (Class<?>) actualTypeArguments[0];
        } else {
            throw new RuntimeException("未知类型：" + resultType);
        }
        return resultType;
    }
}
